package com.mtw.bbs.like.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mtw.bbs.article.mapper.ArticleIndexMapper;
import com.mtw.bbs.article.pojo.entity.ArticleIndex;
import com.mtw.bbs.common.core.constant.BusinessObjectType;
import com.mtw.bbs.common.core.constant.MQConstant;
import com.mtw.bbs.common.core.dto.BaseMQDto;
import com.mtw.bbs.common.core.util.ExecutorUtilz;
import com.mtw.bbs.common.redisBase.util.RedisUtilz;
import com.mtw.bbs.common.redisBase.util.TimeUtilz;
import com.mtw.bbs.constant.LikeConstant;
import com.mtw.bbs.like.mapper.LikeCountMapper;
import com.mtw.bbs.like.pojo.dto.LikeCountDto;
import com.mtw.bbs.like.pojo.entity.LikeCount;
import com.mtw.bbs.like.service.LikeCountService;
import com.mtw.bbs.reply.mapper.ReplyIndexMapper;
import com.mtw.bbs.reply.pojo.entity.ReplyIndex;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.*;

import static com.mtw.bbs.common.core.constant.RedisConstant.KEY_SPACE;
import static com.mtw.bbs.common.core.constant.RedisConstant.NAME_SPACE;

/**
 * 文章点赞逻辑层
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LikeCountServiceImpl extends ServiceImpl<LikeCountMapper, LikeCount> implements LikeCountService {





    private final ExecutorUtilz executorUtilz;
    private final RedissonClient redissonClient;
    private final LikeCountMapper likeCountMapper;
    private final ReplyIndexMapper replyIndexMapper;
    private final RocketMQTemplate rocketMQTemplate;
    private final RedisUtilz<String,Integer> redisUtilz;
    private final ArticleIndexMapper articleIndexMapper;


    /**
     * 获取用户获赞总数
     */
    @Override
    public Integer getUserLikeCount(String uid) {
        String redisKey = LikeConstant.REDIS_LIKE_COUNT_PREFIX +"u:"+uid;
        Integer count = redisUtilz.get(redisKey);
        if (count != null){
            return count;
        }else {
            Long countLong = likeCountMapper.getUserLikeCount(uid);
            if (countLong == null){
                countLong = 0L;
            }
            redisUtilz.set(redisKey,countLong.intValue(),Duration.ofSeconds(60+ TimeUtilz.getRandomSecond()));
            return countLong.intValue();
        }
    }


    /**
     * 获取点赞数
     */
    @Override
    public Integer getLikeCount(LikeCountDto dto) {

        String redisKey = LikeConstant.REDIS_LIKE_COUNT_PREFIX + dto.getObjectType() + NAME_SPACE + dto.getObjectId();
        Integer count = redisUtilz.get(redisKey);
        if (count != null){
            return count;
        }else {
            LambdaQueryWrapper<LikeCount> lqw = new LambdaQueryWrapper<>();
            lqw.select(LikeCount::getLikeCount,LikeCount::getDislikeCount);
            lqw.eq(LikeCount::getObjectType,dto.getObjectType());
            lqw.eq(LikeCount::getObjectId,dto.getObjectId());
            lqw.last(" limit 1");
            List<LikeCount> list = list(lqw);
            if (list.isEmpty()){
                redisUtilz.set(redisKey,0, Duration.ofSeconds(600+ TimeUtilz.getRandomSecond()));
                return 0;
            }else {
                LikeCount likeCount = list.get(0);
                count = likeCount.getLikeCount() - likeCount.getDislikeCount();
                redisUtilz.set(redisKey,count,Duration.ofSeconds(600+ TimeUtilz.getRandomSecond()));
                return count;
            }
        }
    }






    /**
     * 添加点赞统计
     */
    public boolean addLikeCount(LikeCountDto dto){


        String redisKey = LikeConstant.REDIS_LIKE_COUNT_PREFIX + dto.getObjectType() + NAME_SPACE + dto.getObjectId();

        // 查是否已存在
        LambdaQueryWrapper<LikeCount> lqw = new LambdaQueryWrapper<>();
        lqw.eq(LikeCount::getObjectType,dto.getObjectType());
        lqw.eq(LikeCount::getObjectId,dto.getObjectId());
        long count = count(lqw);
        redisUtilz.delete(redisKey);

        boolean result;
        Integer[] likeNumber = getLikeNumber(dto);
        // 新增
        if (count < 1){
            String lockKey = LikeConstant.LOCK_COUNT_PREFIX + dto.getObjectType() + KEY_SPACE + dto.getObjectId();
            RLock lock = redissonClient.getLock(lockKey);
            try {
                lock.lock();
                // 再次查询
                long countAgain = count(lqw);
                // 已存在数据
                if (countAgain > 0){
                    result = this.updateCount(likeNumber,dto);
                }else {

                    LikeCount likeCount = new LikeCount();

                    // 业务对象的创建者
                    if (BusinessObjectType.ARTICLE.equals(dto.getObjectType())){
                        ArticleIndex articleIndex = articleIndexMapper.selectOne(new LambdaQueryWrapper<ArticleIndex>().select(ArticleIndex::getCreatedBy)
                                .eq(ArticleIndex::getId, dto.getObjectId()));
                        if (articleIndex != null){
                            likeCount.setObjectUserId(articleIndex.getCreatedBy());
                        }
                    }else if (BusinessObjectType.REPLY.equals(dto.getObjectType())){
                        ReplyIndex replyIndex = replyIndexMapper.selectOne(new LambdaQueryWrapper<ReplyIndex>().select(ReplyIndex::getCreatedBy)
                                .eq(ReplyIndex::getId, dto.getObjectId()));
                        if (replyIndex != null){
                            likeCount.setObjectUserId(replyIndex.getCreatedBy());
                        }
                    }


                    likeCount.setObjectType(dto.getObjectType());
                    likeCount.setObjectId(dto.getObjectId());
                    likeCount.setLikeCount(likeNumber[0]);
                    likeCount.setDislikeCount(likeNumber[1]);
                    result = this.save(likeCount);
                }
            }catch (Exception e){
                result = false;
                log.error("更新点赞总数失败，type：{}，id：{}",dto.getObjectType() , dto.getObjectId(),e);
            } finally {
                lock.unlock();
            }
        }else {
            result = this.updateCount(likeNumber,dto);
        }

        // 保存、更新成功，发送消息、删除缓存
        if (result){
            this.sendAsyncMQ(dto);
            executorUtilz.delayedTask(()-> redisUtilz.delete(redisKey));
        }
        return result;
    }

    /**
     * 更新点赞总数
     */
    private boolean updateCount(Integer[] likeNumber,LikeCountDto dto){
        String str1 = likeNumber[0] >0?" + "+likeNumber[0]:" - CAST( " + -likeNumber[0] +" AS UNSIGNED)";
        String str2 = likeNumber[1] >0?" + "+likeNumber[1]:" - CAST( " + -likeNumber[1] +" AS UNSIGNED)";
        UpdateWrapper<LikeCount> uw = new UpdateWrapper<>();
        uw.setSql("like_count = ( like_count"+ str1 +"),dislike_count = ( dislike_count"+ str2 +")");
        uw.lambda().eq(LikeCount::getObjectId,dto.getObjectId());
        uw.lambda().eq(LikeCount::getObjectType,dto.getObjectType());
        return this.update(uw);
    }


    /**
     * 设置赞变化数量
     */
    private Integer[] getLikeNumber(LikeCountDto dto){

        Integer[] result = new Integer[]{0,0};
        switch (dto.getOperate()){
            case LIKE_PLUS -> result[0] = 1;
            case LIKE_MINUS -> result[0] = -1;
            case LIKE_TO_DISLIKE -> {
                result[0] = -1;
                result[1] = 1;
            }
            case DISLIKE_PLUS -> result[1] = 1;
            case DISLIKE_MINUS -> result[1] = -1;
            case DISLIKE_TO_LIKE -> {
                result[0] = 1;
                result[1] = -1;
            }
            default -> {}
        }
        return result;
    }


    /**
     * 为下游其他服务发送点赞、点赞数消息
     */
    private void sendAsyncMQ(LikeCountDto dto){

        //点赞数变化数量
        int number = 0;
        switch (dto.getOperate()){
            case LIKE_PLUS, DISLIKE_MINUS -> number = 1;
            case LIKE_MINUS, DISLIKE_PLUS -> number = -1;
            case LIKE_TO_DISLIKE ->  number = -2;
            case DISLIKE_TO_LIKE ->  number = 2;
            default -> {}
        }
        BaseMQDto form = new BaseMQDto();
        form.setObjectType(dto.getObjectType());
        form.setObjectId(dto.getObjectId());
        form.setCount(number);
        // MQ似乎有点多余
        log.error("发送MQ,{}",form);
        try {
            rocketMQTemplate.syncSend(MQConstant.LIKE_TOPIC+":"+dto.getObjectType(), MessageBuilder.withPayload(JSONObject.toJSON(form)).build());
        }catch (Exception e){
            log.error("发送MQ异常：",e);
        }

    }







}
